<template>
    <div class="logic-type-select-container" :id="id" :key="id">
        <div class="flex-v-center text-placeholder" v-if="app.dependencyInitializing">
            <tnxel-icon class="mx-1" value="Loading" style="margin-top: 1px;"/>
            <span class="fs-p90" :title="modelValue">{{ typeClass ? typeClass.simpleName : '依赖库加载中' }}</span>
        </div>
        <div class="mx-2 text-placeholder"
            :title="TypeKind.PRIMITIVE.equals(typeClass.kind) ? undefined : typeClass.getName()"
            v-else-if="readOnly">
            {{ typeClass.simpleName }}
        </div>
        <el-cascader v-model="model"
            :props="{value: 'id', label: 'simpleName', emitPath:false}"
            :options="options"
            :title="modelValue"
            separator="."
            popper-class="logic-type-select-popper"
            :placeholder="disabled ? ' ' : '输入关键字过滤'"
            filterable
            :filter-method="queryClass"
            :clearable="extended || clearable"
            :disabled="disabled"
            :show-all-levels="false"
            @change="onChange"
            v-else>
            <template #default="{data}">
                <NodeIcon class="me-1" :type="data.type"/>
                <span class="text-muted" v-if="data.favoriteOrdinal > 0 && data.packageName">
                    {{ data.packageName }}.
                </span>
                <span class="ms-0">{{ data.simpleName }}</span>
                <el-tag type="info" class="ms-1" size="small" v-if="data.deprecated">Deprecated</el-tag>
            </template>
        </el-cascader>
    </div>
</template>

<script>
import tnx from '@/tnx.js';
import ModelingApp from '@/logic/modeling/entity/ModelingApp.js';
import TypeKind from '@/logic/modeling/enums/TypeKind.js';
import {
    STRING_CLASS_NAME,
    LONG_CLASS_NAME,
    INTEGER_CLASS_NAME
} from '@/logic/modeling/util/dependency-lib-jdk.js';
import GenerateUtil from '@/logic/modeling/util/generate.js';

import NodeIcon from './NodeIcon.vue';

const declaredGenericTypeId = '[self]';
const declaredGenericTypeIdPrefix = declaredGenericTypeId + '.';

export default {
    DECLARED_GENERIC_TYPE_ID_PREFIX: declaredGenericTypeIdPrefix,
    name: 'LogicTypeSelect',
    components: {NodeIcon},
    props: {
        modelValue: String, // classId
        app: {
            type: ModelingApp,
            required: true,
        },
        readOnly: {
            type: Boolean,
            default: false,
        },
        kind: { // 所在类的性质
            type: TypeKind,
            required: true,
        },
        primary: { // 当前属性是否主键，主键属性只有少数类型可选
            type: Boolean,
            default: false,
        },
        extended: { // 是否继承场景
            type: Boolean,
            default: false,
        },
        clearable: {
            type: Boolean,
            default: false,
        },
        disabled: {
            type: Boolean,
            default: false,
        },
        declaredGenericTypes: Array, // 额外支持的声明泛型集
    },
    emits: ['update:modelValue', 'change'],
    data() {
        return {
            id: tnx.util.string.uuid32(),
            model: this.modelValue,
            options: [],
        };
    },
    computed: {
        TypeKind() {
            return TypeKind;
        },
        typeClass() {
            // 依赖加载中时，可以显示id等于name的类型，即modelValue长度不等于32的
            if (this.app.dependencyInitializing) {
                if (this.modelValue && this.modelValue.length !== 32) {
                    let packageName = '';
                    let index = this.modelValue.lastIndexOf('.');
                    if (index >= 0) {
                        packageName = this.modelValue.substring(0, index);
                    }
                    let simpleName = this.modelValue.substring(index + 1);
                    return {packageName, simpleName};
                }
                return null;
            }
            return this.app.findClass(this.modelValue, true, true);
        },
    },
    watch: {
        model() {
            this.$emit('update:modelValue', this.model);
        },
        modelValue() {
            this.model = this.modelValue;
        },
        app() {
            this.init();
        },
        kind() {
            this.init();
        },
        primary() {
            this.init();
        },
        extended() {
            this.init();
        },
        clearable() {
            this.init();
        },
        declaredGenericTypes() {
            this.init();
        },
    },
    mounted() {
        this.init();
        tnx.app.eventBus.on('app-changed', this.onAppChanged);
    },
    unmounted() {
        tnx.app.eventBus.off('app-changed', this.onAppChanged);
    },
    methods: {
        onAppChanged(appName) {
            if (this.app.name === appName) {
                this.init();
                this.id = tnx.util.string.uuid32();
            }
        },
        init() {
            this.options = [];
            if (this.readOnly) {

            } else {
                if (this.extended) {
                    this.options.push({
                        type: 'info',
                        packageName: '',
                        simpleName: '已排除不可被继承的类型',
                        name: 'info-only-extendable',
                        disabled: true,
                    });
                }
                if (this.declaredGenericTypes?.length) {
                    let genericTypesDataList = [];
                    this.options.push({
                        type: 'package',
                        simpleName: '[当前类泛型]',
                        name: declaredGenericTypeId,
                        children: genericTypesDataList,
                    });
                    for (let genericType of this.declaredGenericTypes) {
                        let genericTypeNodeName = declaredGenericTypeIdPrefix + genericType.parameter;
                        let data = Object.assign({}, genericType, {
                            id: genericTypeNodeName,
                            type: TypeKind.GENERIC.name,
                            simpleName: genericType.parameter,
                            name: genericTypeNodeName,
                        });
                        genericTypesDataList.push(data);
                    }
                }
                for (let dependency of this.app.dependencies) {
                    // 抽出偏爱加入第一级
                    for (let clazz of dependency.classes) {
                        if (clazz.favoriteOrdinal > 0 && this.isValid(clazz)) {
                            this.addNodeData(this.options, this.toNodeData(clazz));
                        }
                    }
                    // 再加入其它
                    for (let clazz of dependency.classes) {
                        if (clazz.favoriteOrdinal <= 0) {
                            let kind = clazz.getKind();
                            if (TypeKind.PRIMITIVE.equals(kind) && this.isValid(clazz)) {
                                this.addNodeData(this.options, this.toNodeData(clazz));
                            } else {
                                this.addReferenceClassNode(clazz);
                            }
                        }
                    }
                }

                // 加入当前建模工程中的类
                for (let clazz of this.app.classes) {
                    this.addReferenceClassNode(clazz);
                }

                // 整理节点，合并只有一个子节点的包
                for (let data of this.options) {
                    this.pullUpChildrenIfOnlyOne(data);
                }

                this.sort(this.options);
                this.onChange();
            }
        },
        isValid(clazz) {
            if (this.extended) { // 继承场景
                if (clazz.isFinal()) {
                    return false;
                }
                let packageName = clazz.getPackageName ? clazz.getPackageName() : clazz.packageName;
                // 固定不能继承的包
                return !['java.lang'].includes(packageName);
            }

            // 引用场景
            let kind = clazz.getKind();
            // 关系不能被引用
            if (TypeKind.RELATION.equals(kind)) {
                return false;
            }
            if (this.primary) { // 当前是主键的判断
                // 当前类为关系时，可将下列固定类型以及单体类作为主键类型
                if (TypeKind.RELATION.equals(this.kind)) {
                    return [STRING_CLASS_NAME, 'long', 'int'].includes(clazz.getId()) || TypeKind.UNITY.equals(kind);
                }
                // 其它性质时，可将下列固定类型作为主键类型
                return [STRING_CLASS_NAME, LONG_CLASS_NAME, INTEGER_CLASS_NAME].includes(clazz.getId());
            }
            // 当前不是主键
            if (TypeKind.VALUE.equals(this.kind)) {
                // 当前类为值模型，则不能再引用值模型
                return !TypeKind.VALUE.equals(kind);
            } else if (TypeKind.PLAIN.equals(this.kind)) {
                // 当前为POJO类，则只能引用一般引用场景类型
                return kind.supports('base');
            } else if (TypeKind.GENERIC.equals(this.kind)) {
                // 当前为泛型，则不能引用原生类型
                return !TypeKind.PRIMITIVE.equals(kind);
            }
            // POJO可以通过指定转换器进行映射，因而可以引用
            return true;
        },
        addReferenceClassNode(clazz) {
            if (this.isValid(clazz)) {
                let nodes = this.options;
                let packageName = clazz.getPackageName ? clazz.getPackageName() : clazz.packageName;
                let simpleNames = packageName.split('.');
                for (let i = 0; i < simpleNames.length; i++) {
                    let simpleName = simpleNames[i];
                    let node = nodes.find(n => n.simpleName === simpleName);
                    if (!node) {
                        node = {
                            type: 'package',
                            name: simpleNames.slice(0, i + 1).join('.'),
                            simpleName: simpleName,
                            children: [],
                        }
                        nodes.push(node);
                    }
                    nodes = node.children;
                }
                this.addNodeData(nodes, this.toNodeData(clazz));
                this.sort(nodes);
            }
        },
        sort(nodeDataList) {
            nodeDataList.sort((data1, data2) => {
                let typeOrder1 = this.getOrder(data1);
                let typeOrder2 = this.getOrder(data2);
                if (typeOrder1 !== typeOrder2) {
                    return typeOrder1 - typeOrder2;
                } else {
                    return data1.simpleName.localeCompare(data2.simpleName);
                }
            });
        },
        getOrder(data) {
            // 提示信息排在最前
            if (data.type === 'info') {
                return 0;
            }
            // 偏爱排次之
            if (data.favoriteOrdinal > 0) {
                return data.favoriteOrdinal;
            }
            // 其它的附加10000的偏移值，以排在偏爱后面，这意味着最多可以支持10000个偏爱，实际上已经足够了
            // 原生类型排在其它类型前面，其它类型混排
            if (data.type === TypeKind.PRIMITIVE.name) {
                return 10000;
            }
            if (data.type !== 'package') {
                return 10001;
            }
            return 10002;
        },
        toNodeData(clazz) {
            return Object.assign({}, clazz, {
                id: clazz.getId ? clazz.getId() : clazz.id,
                type: clazz.getKind().name,
                packageName: clazz.getPackageName ? clazz.getPackageName() : clazz.packageName,
                simpleName: clazz.getSimpleName ? clazz.getSimpleName() : clazz.simpleName,
                name: clazz.getName(),
                final: clazz.isFinal(),
            });
        },
        addNodeData(array, data) {
            if (!array.contains(e => e.name === data.name)) {
                array.push(data);
            }
        },
        pullUpChildrenIfOnlyOne(data) {
            if (data.type === 'package') {
                while (data.children.length === 1 && data.children[0].type === 'package') {
                    let child = data.children[0];
                    data.name += '.' + child.simpleName;
                    data.simpleName += '.' + child.simpleName;
                    data.children = child.children || [];
                }
                for (let child of data.children) {
                    this.pullUpChildrenIfOnlyOne(child)
                }
            }
        },
        findClass(className) {
            for (let dependency of this.app.dependencies) {
                let clazz = dependency.findClass(className);
                if (clazz) {
                    return clazz;
                }
            }

            let clazz = this.app.findClass(className);
            if (clazz) {
                return clazz;
            }

            return undefined;
        },
        queryClass(node, keyword) {
            if (node.data.type === TypeKind.GENERIC.name) {
                return node.data.simpleName.toLowerCase().contains(keyword.toLowerCase());
            } else {
                let classes = this.app.queryClass(keyword);
                return classes.some(clazz => clazz.getId() === node.data.id);
            }
        },
        onChange() {
            let oldValue = this.modelValue;
            this.$nextTick(() => {
                let newValue = this.modelValue;
                if (oldValue !== newValue) {
                    this.$emit('change', newValue);
                }
                setTimeout(() => {
                    let container = document.getElementById(this.id);
                    if (container) {
                        let input = container.getElementsByClassName('el-input__inner')[0];
                        if (input) {
                            let clazz = this.findClass(this.modelValue);
                            if (clazz) {
                                let packageName = clazz.getPackageName ? clazz.getPackageName() : clazz.packageName;
                                if (packageName) {
                                    let className = clazz.getName();
                                    if (clazz.declaredGenericTypes) {
                                        className += GenerateUtil.generateGenericTypesCaption(this.app,
                                            clazz.declaredGenericTypes);
                                    }
                                    input.setAttribute('title', className);
                                } else {
                                    input.removeAttribute('title');
                                }
                                // 继承场景下，输入框中显示泛型文本
                                if (this.extended && clazz.declaredGenericTypes) {
                                    input.value = clazz.simpleName + GenerateUtil.generateGenericTypesCaption(this.app,
                                        clazz.declaredGenericTypes, true);
                                }
                            }
                        }
                    }
                });
            });
        },
    }
}
</script>

<style lang="scss">
.logic-type-select-container {
    width: 100%;

    .el-cascader {
        width: 100%;
    }
}

.logic-type-select-popper {
    .el-cascader-node__label {
        display: flex;
        align-items: center;
    }

    .el-cascader-menu__wrap.el-scrollbar__wrap {
        height: auto;
        max-height: 200px;
    }
}
</style>
